﻿using System;
using System.Collections.Generic;

namespace Borg.Framework.CompositionBinding
{
    public class CompositionBinding<T>
    {
        private ICompositionBehavior<T> _behavior;
        private ICompositionControl<T> _control;

        public event EventHandler<ItemEventArgs<T>> ComposeItem;
        public event EventHandler<ItemEventArgs<T>> DecomposeItem;

        public event EventHandler<ItemEventArgs<T>> ItemAdded;
        public event EventHandler<ItemEventArgs<T>> ItemRemoved;

        public event EventHandler<ItemEventArgs<T>> ItemComposed;
        public event EventHandler<ItemEventArgs<T>> ItemDecomposed;

        private void DeregisterBehaviorEvents()
        {
            _behavior.ItemAdded -= OnItemAdded;
            _behavior.ItemRemoved -= OnItemRemoved;
            _behavior.ItemComposed -= OnItemComposed;
            _behavior.ItemDecomposed -= OnItemDecomposed;
        }

        private void DeregisterControlEvents()
        {
            _control.ComposeItem -= OnComposeItem;
            _control.DecomposeItem -= OnDecomposeItem;
        }

        private void DisableBinding()
        {
            if (_control == null || _behavior == null)
                return;

            DeregisterBehaviorEvents();
            DeregisterControlEvents();
        }

        private void EnableBinding()
        {
            if (_control == null || _behavior == null)
                return;

            RegisterBehaviorEvents();
            RegisterControlEvents();
            InitControl();
        }

        private void InitControl()
        {
            _control.Clear();

            foreach (var local in _behavior.Items)
                OnItemAdded(_control, new ItemEventArgs<T>(local));

            foreach (var local in _behavior.ComposedItems)
                OnItemComposed(_control, new ItemEventArgs<T>(local));
        }

        private static void InvokeHandler(ItemEventHandler<T> handler, T item)
        {
            handler(item);
        }

        private void OnComposeItem(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_behavior.Compose, e.Item);
            OnEvent(ComposeItem, sender, e);
        }

        private void OnDecomposeItem(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_behavior.Decompose, e.Item);
            OnEvent(DecomposeItem, sender, e);
        }

        private static void OnEvent(EventHandler<ItemEventArgs<T>> handler,
            object sender, ItemEventArgs<T> args)
        {
            if (handler != null)
                handler(sender, args);
        }

        private void OnItemAdded(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_control.Add, e.Item);
            OnEvent(ItemAdded, sender, e);
        }

        private void OnItemComposed(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_control.SetComposed, e.Item);
            OnEvent(ItemComposed, sender, e);
        }

        private void OnItemDecomposed(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_control.SetDecomposed, e.Item);
            OnEvent(ItemDecomposed, sender, e);
        }

        private void OnItemRemoved(object sender, ItemEventArgs<T> e)
        {
            InvokeHandler(_control.Remove, e.Item);
            OnEvent(ItemRemoved, sender, e);
        }

        private void RegisterBehaviorEvents()
        {
            _behavior.ItemAdded += OnItemAdded;
            _behavior.ItemRemoved += OnItemRemoved;
            _behavior.ItemComposed += OnItemComposed;
            _behavior.ItemDecomposed += OnItemDecomposed;
        }

        private void RegisterControlEvents()
        {
            _control.ComposeItem += OnComposeItem;
            _control.DecomposeItem += OnDecomposeItem;
        }

        public ICompositionBehavior<T> Behavior
        {
            get { return _behavior; }
            set
            {
                if (_behavior == value)
                    return;

                DisableBinding();
                _behavior = value;
                EnableBinding();
            }
        }

        public IEnumerable<T> ComposedItems
        {
            get
            {
                return _behavior != null
                    ? _behavior.ComposedItems
                    : new List<T>();
            }
        }

        public ICompositionControl<T> Control
        {
            get { return _control; }
            set
            {
                if (_control == value)
                    return;

                DisableBinding();
                _control = value;
                EnableBinding();
            }
        }

        public IEnumerable<T> Items
        {
            get
            {
                return _behavior != null
                    ? _behavior.Items
                    : new List<T>();
            }
        }

        private delegate void ItemEventHandler<T>(T item);
    }
}

